/*
This file is part of buco.  
Copyright (c) 2012 Christopher Pawelzik <christopher@cpawelzik.com>

Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 
*/ 
package buco.core.api;

import java.io.*;
import java.util.*;

import org.json.simple.JSONObject;

import buco.core.internal.*;

/**
 * Represents an OSGi Bundle.
 *
 */
public class Bundle implements MetadataVisitable {	
	private String bundleSymbolicName;
	private String bundleName; 
	private String bundleDescription; 
	private Version bundleVersion; 
	private String bcl;
	private int bundeID;
	private int fileID; 
	
	private List<Requirement> requirements;	
	private List<Capability> capabilities;

	// Custom properties, that can be stored for a bundle by an API-consumer (i.e. position, width, height)
	private HashMap<String, Object> properties; 
	
	private HashMap<String, List<JSONObject>> semanticConstraints;
	 
	public Bundle() {
		this.requirements = new ArrayList<Requirement>(); 
		this.capabilities = new ArrayList<Capability>();
		this.properties = new HashMap<String, Object>(); 
		this.semanticConstraints = new HashMap<String, List<JSONObject>>(); 
	}
	
	public Bundle(String symbolicName) { 
		this();
		this.setBundleSymbolicName(symbolicName); 
	}
	
	@Override
	public void accept(MetadataVisitor visitor) {
		for(Requirement req : requirements) { 
			req.accept(visitor); 
		}
		
		for(Capability cap : capabilities) { 
			cap.accept(visitor);
		}
	}
	
	public void addRequirement(Requirement requirement) {
		this.requirements.add(requirement); 
	}
	
	public void addCapability(Capability capability) {
		this.capabilities.add(capability); 		
	}
	
	public void addSemanticConstraint(String identifier, JSONObject object) { 
		List<JSONObject> objects = null; 
		
		if(!this.semanticConstraints.containsKey(identifier)) { 
			objects = new ArrayList<JSONObject>(); 
			this.semanticConstraints.put(identifier, objects);
		} else { 
			objects = this.semanticConstraints.get(identifier);
		}
		
		objects.add(object);
	}
	
	public static Bundle fromJarFile(File jarFile) {
		try {
			return JARParser.parse(jarFile);
		} catch (IOException e) {			
			e.printStackTrace();
			return null;
		}
	}

	public String getBundleName() {
		return bundleName;
	}

	public void setBundleName(String bundleName) {
		this.bundleName = bundleName;
	}

	public String getBundleSymbolicName() {
		return bundleSymbolicName;
	}
	
	public void setBundleSymbolicName(String bundleSymbolicName) {
		this.bundleSymbolicName = bundleSymbolicName;
	}

	public Version getBundleVersion() {
		return bundleVersion;
	}

	public void setBundleVersion(Version bundleVersion) {
		this.bundleVersion = bundleVersion;
	}	
	
	public List<Requirement> getRequirements() {
		return this.requirements; 
	}
	
	public List<Capability> getCapabilities() {
		return this.capabilities; 				
	}
	
	public int getBundleID() { 
		return this.bundeID; 
	}
	
	public void setBundleID(int id) { 
		this.bundeID = id; 
	}
	
	public void setCustomProperty(String name, Object value) {
		this.properties.put(name, value); 
	}
	
	public Object getCustomProperty(String name) {
		if(!this.properties.containsKey(name))
			return null; 
		
		return this.properties.get(name); 				
	}

	public int getFileID() {
		return fileID;
	}

	public void setFileID(int fileID) {
		this.fileID = fileID;
	}

	public String getBundleDescription() {
		return bundleDescription;
	}

	public void setBundleDescription(String bundleDescription) {
		this.bundleDescription = bundleDescription;
	}
	
	public <T extends SemanticConstraint> T getSemanticConstraint(String identifier, Class<T> clazz) { 
		List<T> list = getSemanticConstraints(identifier, clazz); 
		if(list.size() == 0)
			return null; 
		
		return list.get(0); 
	}
	
	public <T extends SemanticConstraint> List<T> getSemanticConstraints(String identifier, Class<T> clazz) { 
		List<T> list = new ArrayList<T>(); 
		try {
			if(this.semanticConstraints.containsKey(identifier)) {
				for(JSONObject object : this.semanticConstraints.get(identifier)) { 
					T constraint = clazz.newInstance();
					try {
						constraint.deserialize(object);
					} catch (Exception e) {
						continue;
					} 
					list.add(constraint);
				}
			}
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
			return null;
		} 
		
		return list; 
	}
	
	public String getBcl() { 
		return this.bcl;
	}
	
	public void setBcl(String bcl) { 
		this.bcl = bcl;
	}
	
	public boolean satisfiesRequirement(Requirement requirement) {
		for(Capability cap : this.capabilities) {
			if(requirement.isSatisfiedBy(this, cap)) {  
				return true;		
			}
		}		
		return false; 
	}
}
